package com.gmall.seckill.controller;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.gmall.common.result.Result;
import com.gmall.common.utils.IdWorker;
import com.gmall.model.seckill.SeckillGoods;
import com.gmall.seckill.common.RedisConst;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("api/seckill")
public class SecKillController {


    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    RedissonClient redissonClient;

    @Autowired
    IdWorker idWorker;

    @Autowired
    RabbitTemplate rabbitTemplate;
    /**
     * 用户能看到的所有能够购买的商品
     *
     * 7天后 所有可以参与秒杀的商品全部查出来
     * 要求前端列表展示所有商品，如果已经结束秒杀，显示已结束
     * 正在进行中，显示剩余多少时间结束 30分钟
     * 未开始，显示距离开始还有多少开始 10分钟
     * @return
     */
    @GetMapping("/findAvaliableSeckillGoods")
    public Result<List<SeckillGoods>> findAvaliableSeckillGoods(){

        //7天后 所有可以参与秒杀的商品全部查出来
        //从Redis查询
        // 正在进行    结束时间<now()
        // 未开始      开始时间>now()
        // 已经结束    结束时间<now()
        //直接从Redis中返回
        List<SeckillGoods> list =
                (List<SeckillGoods>)redisTemplate.opsForHash().values(RedisConst.SEC_KILL_REDIS_KEY);

        return Result.ok(list);
    }

    @GetMapping("/test/{skuId}")
    @SentinelResource(value = "seckill_test",
            blockHandler = "secKill_test_blockHandler",
            fallback = "secKill_test_fallbackHandler")
    public Result<String> secKill_test(@RequestHeader Long userId,
                                  @PathVariable Long skuId){

        int i = 10/0;
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("userId = " + userId + ", skuId = " + skuId);
        return Result.ok("秒杀成功");
    }

    /**
     * 出现限流的执行业务逻辑
     * @param userId
     * @param skuId
     * @param e
     * @return
     */
    public Result secKill_test_blockHandler(Long userId, Long skuId,
                                     BlockException e){
        System.out.println("secKill_test_blockHandler");
        e.printStackTrace();
        System.out.println("userId = " + userId + ", skuId = " + skuId);
        return Result.fail("秒杀失败，请稍后再试1");
    }

    /**
     * 出现熔断的执行业务逻辑
     * @param userId
     * @param skuId
     * @param e
     * @return
     */
    public Result secKill_test_fallbackHandler(Long userId, Long skuId,
                                 Throwable e){
        System.out.println("secKill_test_fallbackHandler");
        e.printStackTrace();
        System.out.println("userId = " + userId + ", skuId = " + skuId);
        return Result.fail("秒杀失败，请稍后再试2");
    }


    @GetMapping("/secKill/{skuId}")
    public Result<String> secKill(@RequestHeader Long userId,
                                  @PathVariable Long skuId){
        RLock lock = redissonClient.getLock(RedisConst.SEC_KILL_LOCK_KEY + skuId);
        //加锁
        //1、只操作Redis
        //2、判断是否开始，如果尚未开始，直接返回""Result.fail("尚未开始");
        //3、判断是否结束，如果已经结束，直接返回""Result.fail("已经结束");
        //4、查询SKU的剩余数量 判断数量是否大于0，如果小于0，直接返回""Result.fail("已经抢完");
        //5.判断该用户是否已经抢购过，如果已经抢购过，直接返回""Result.fail("你已经抢购过");
        //记录那个用户抢到了
        //并且数量-1
        try {
            lock.lock();//加锁
            SeckillGoods seckillGoods = (SeckillGoods)
                    redisTemplate.opsForHash().get(RedisConst.SEC_KILL_REDIS_KEY, skuId.toString());
            Date now = new Date();
            Date startTime = seckillGoods.getStartTime();
            Date endTime = seckillGoods.getEndTime();
            if (now.before(startTime)) {
                return Result.fail("尚未开始");
            }
            if (now.after(endTime)) {
                return Result.fail("已经结束");
            }
            Integer publishCount = seckillGoods.getNum();//发型数量
            Integer stockCount = (Integer) redisTemplate.opsForValue().get(RedisConst.SEC_KILL_REDIS_STOCK_COUNT_KEY + skuId);
            if (stockCount == null) {
                stockCount = seckillGoods.getStockCount();
            }
            if (stockCount <= 0) {
                return Result.fail("已经抢完");
            }
            Integer skuUserNum =
                    (Integer) redisTemplate.opsForHash().get(RedisConst.SEC_KILL_REDIS_SKU_USER_KEY + skuId, userId.toString());
            //还有剩余
            if (skuUserNum == null) {
                skuUserNum = 0;
            }

            if (skuUserNum>0) {
                return Result.fail("您已经抢到上限");
            }
            //有剩余,库存-1
            redisTemplate.opsForValue().decrement(RedisConst.SEC_KILL_REDIS_STOCK_COUNT_KEY + skuId, 1);

            redisTemplate.opsForHash().increment(
                    RedisConst.SEC_KILL_REDIS_SKU_USER_KEY + skuId,
                    userId.toString(), 1);
                Map map = new HashMap<>();
                map.put("skuId", skuId);
                map.put("userId", userId);
            //发送可靠的消息
            rabbitTemplate.convertAndSend("", "Queue_Coupon",
                    map,
                    (message) -> {
                        message.getMessageProperties().setMessageId(
                                String.valueOf(idWorker.nextId()));
                        return message;
                    });//网络IO
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();//解锁 一定要在finally写
        }

        //秒杀后
        //发一条可靠地消息
        return Result.ok("恭喜您抢购成功");
    }
}
